package com.hsl.study.utils;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author: hsl
 * @CreateTime: 2025-02-14  19:28
 */

@Slf4j
public class PerformanceRunnerUtil {
    //1 静态内部类，封装返回压测结果集PerformanceRunnerResult
    @Data
    public static class PerformanceRunnerResult {
        // 请求总数
        private int requests;
        // 并发量
        private int concurrency;
        // 成功请求数
        private int successRequests;
        // 失败请求数
        private int failRequests;
        // 请求总耗时(ms)
        private int timeTakenForTests;
        // 每秒请求数（吞吐量）
        private float requestsPerSecond;
        // 每个请求平均耗时(ms)
        private float timePerRequest;
        // 最快的请求耗时(ms)
        private float fastestCostTime;
        // 最慢的请求耗时(ms)
        private float slowestCostTime;
    }


    // 2  定义压测方法

    /**
     * @param requests    总请求数
     * @param concurrency 并发数
     * @param runnable    需要执行的压测的业务逻辑代码，直接作为线程任务开干
     * @param <T>
     * @return 压测结果 {@link  PerformanceRunnerResult}
     * @throws InterruptedException
     */
    public static <T> PerformanceRunnerResult performanceRunnerResult(int requests, int concurrency, Runnable runnable) throws InterruptedException {
        log.info("======PerformanceRunnerUtil压力测试开始=======");
        //静态内部类，返回压测结果集
        PerformanceRunnerResult performanceRunnerResult = new PerformanceRunnerResult();
        // 2.1 线程池创建线程 核心数直接==最大线程数 直接拉满
        //  为了通用 使用JUC的 ThreadPoolExecutor 不用spring的ThreadPoolTaskExecutor
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                concurrency,
                concurrency,
                //全部存活
                0L,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingDeque<>()
        );
        // 2.2 启动所有核心线程
        threadPoolExecutor.prestartAllCoreThreads();
        // 2.3 创建一个 CountDownLatch 用于租的当前线程池 等待所有线程都执行完毕后，让当前线程往下走  倒计时
        CountDownLatch countDownLatch = new CountDownLatch(requests);
        // 2.4 成功请求数     最快耗时   最慢耗时
        AtomicInteger successRequests = new AtomicInteger();
        AtomicInteger fastestCostTime = new AtomicInteger(Integer.MAX_VALUE);
        AtomicInteger slowestCostTime = new AtomicInteger(Integer.MIN_VALUE);
        // 开始时间
        long startTime = System.currentTimeMillis();

        try {
            for (int i = 0; i < requests; i++) {
                threadPoolExecutor.execute(() -> {
                    try {
                        // 某个线程开始时间    顾客坐到窗口的时间
                        long requestStartTime = System.currentTimeMillis();
                        // 执行压测的耗时
                        runnable.run();
                        // 执行压测的耗时
                        int costTime = (int) (System.currentTimeMillis() - requestStartTime);
                        //请求最快耗时
                        setFastestCostTime(fastestCostTime, costTime);
                        // 请求最慢耗时
                        setSlowestCostTimeCostTime(slowestCostTime, costTime);
                        //成功请求数 +1
                        successRequests.incrementAndGet();//++i
                    } catch (Exception e) {
                        log.info(e.getMessage());
                    } finally {
                        countDownLatch.countDown();
                    }
                });
            }
            //阻塞当前线程 ，等压测结束后，唤醒此方法 线程继续往下走
            countDownLatch.await();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭线程池
            threadPoolExecutor.shutdown();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("=============costTime:" + (endTime - startTime) + "毫秒");
        // 组装结果并且返回
        performanceRunnerResult.setRequests(requests);
        performanceRunnerResult.setConcurrency(concurrency);
        performanceRunnerResult.setSuccessRequests(successRequests.get());
        performanceRunnerResult.setFailRequests(requests - performanceRunnerResult.getSuccessRequests());
        performanceRunnerResult.setTimeTakenForTests((int) (endTime - startTime));
        performanceRunnerResult.setRequestsPerSecond((float) requests * 1000f / (float) (performanceRunnerResult.getTimeTakenForTests()));
        performanceRunnerResult.setTimePerRequest((float) performanceRunnerResult.getTimeTakenForTests() / (float) requests);
        performanceRunnerResult.setFastestCostTime(fastestCostTime.get());
        performanceRunnerResult.setSlowestCostTime(slowestCostTime.get());

        return performanceRunnerResult;
    }


    /**
     * @param fastestCostTime
     * @param costTime        比较并交换 CAS
     */
    private static void setFastestCostTime(AtomicInteger fastestCostTime, int costTime) {
        while (true) {
            int fsCostTime = fastestCostTime.get();
            if (fsCostTime < costTime) {
                break;
            }
            if (fastestCostTime.compareAndSet(fsCostTime, costTime)) {
                break;
            }
        }
    }


    private static void setSlowestCostTimeCostTime(AtomicInteger slowestCostTime, int costTime) {
        while (true) {
            int slCostTime = slowestCostTime.get();
            if (slCostTime > costTime) {
                break;
            }
            if (slowestCostTime.compareAndSet(slCostTime, costTime)) {
                break;
            }
        }
    }
}
